﻿using LightCAD.Core;
using LightCAD.Core.Elements;
using LightCAD.Model;
using LightCAD.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using LightCAD.MathLib;

namespace QdWater
{
    internal class LcPipe : DirectComponent, IElement3D
    {
        public object Rt3DAction { get; set; } = new Element3DAction();
        public Line2d BaseLine => (Line2d)this.BaseCurve;
        //TODO 关于默认值 需要在创建时指定，因为需要根据环境給值
        public double Width { get; set; }
        public double Height { get; set; }
        public double Bottom { get; set; }
        public double CenterOffset { get; set; }
        public bool IsReverse { get; set; }

        public LcPipe()
        {
            this.Type = WaterElementType.Pipe;
            this.linkIds = new List<long>();
        }
        public List<long> linkIds { get; set; }


        public LcPipe(Vector2 start, Vector2 end)
        {
            this.Curves.Add(new Line2d { Start = start, End = end, Source = this });
        }
        public static List<Line2d> GetShapeCurves(Line2d line, double width)
        {

            double x1 = line.Start.X;
            double y1 = line.Start.Y;
            double x2 = line.End.X;
            double y2 = line.End.Y;
            double setoff = (width) / 2;
            Vector2 p1=new Vector2();
            Vector2 p2=new Vector2();
            Vector2 p3=new Vector2();
            Vector2 p4=new Vector2();

            if (x1 == x2)
            {
                p1.Y = y1;
                p1.X = x1 + setoff;
                p2.Y = y1;
                p2.X = x1 - setoff;
                p3.Y = y2;
                p3.X = x2 - setoff;
                p4.Y = y2;
                p4.X = x2 + setoff;
            }
            else
            if (y1 == y2)
            {
                p1.Y = y1 + setoff;
                p1.X = x1;
                p2.Y = y1 - setoff;
                p2.X = x1;
                p3.Y = y2 - setoff;
                p3.X = x2;
                p4.Y = y2 + setoff;
                p4.X = x2;
            }
            else
            {
                double k = (y2 - y1) / (x2 - x1);
                double k1 = -1 / k;
                double b1 = y1 - k1 * x1;



                double xA = x1;
                double yA = y1;
                double xB = x1 + Math.Pow(width, 2);
                double yB = k1 * xB + b1;

                // 给定距离
                double distance = setoff;

                // 计算线段的方向向量
                double directionX = xB - xA;
                double directionY = yB - yA;

                // 计算方向向量的模长
                double length = Math.Sqrt(directionX * directionX + directionY * directionY);

                // 标准化方向向量（单位化）
                double unitDirectionX = directionX / length;
                double unitDirectionY = directionY / length;

                // 计算目标点的坐标
                p1.X = xA + distance * unitDirectionX;
                p1.Y = yA + distance * unitDirectionY;


                Matrix3 matrix3 = Matrix3.GetMove(p1, line.Start);
                p2 = matrix3.MultiplyPoint(line.Start);

                matrix3 = Matrix3.GetMove(line.Start, line.End);

                p3 = matrix3.MultiplyPoint(p2);
                p4 = matrix3.MultiplyPoint(p1);


            }

            List<Line2d> line2Ds = new List<Line2d>();
            Line2d line2D = new Line2d();
            line2D.Start = p1;
            line2D.End = p2;

            line2Ds.Add(line2D);
            line2D = new Line2d();
            line2D.Start = p2;
            line2D.End = p3;

            line2Ds.Add(line2D);
            line2D = new Line2d();
            line2D.Start = p3;
            line2D.End = p4;

            line2Ds.Add(line2D);
            line2D = new Line2d();
            line2D.Start = p4;
            line2D.End = p1;

            line2Ds.Add(line2D);
            return line2Ds;
        }
        public void set()
        {

        }
        public void CreateShapeCurves()
        {
            this.Points.Clear();
            this.Curves.Clear();
            List<Line2d> line2Ds =
            GetShapeCurves(this.BaseLine, this.Width);
            this.Points.Add(line2Ds[1].Start);
            this.Points.Add(line2Ds[1].End);
            this.Points.Add(line2Ds[3].Start);
            this.Points.Add(line2Ds[3].End);
            this.Curves.AddRange(line2Ds);
        }
        public override void ResetShapeCurves()
        {


        }
        public void AddLcComponent(LcElement lcElement, string name, AssociateType type)
        {
            AssociateElement associateElement = new AssociateElement();
            associateElement.Element = Activator.CreateInstance(lcElement.GetType()) as LcElement;
            associateElement.Element.Copy(lcElement); //Clone后Id会发生改变
            associateElement.Tag = lcElement.Id; //将原始的LcElement.Id存下来，方便Remove时来匹配
            associateElement.Type = type;
            associateElement.Name = name;
            this.AssociateElements.Add(associateElement);

        }
        public void RemoveLcComponent(LcElement lcElement, string name, AssociateType type)
        {

            AssociateElement associateElement = this.AssociateElements.Find(e => (long)e.Tag == lcElement.Id);

            if (associateElement == null)
                return;

            this.AssociateElements.Remove(associateElement);
        }
        public void UpdateLcComponent(LcElement lcElement, string name, AssociateType type)
        {
            RemoveLcComponent(lcElement, name, type);
            AddLcComponent(lcElement, name, type);

            //AssociateElement associateEl = null;
            //foreach (var item in this.AssociateElements)
            //{
            //    if (item.Element == lcElement && item.Type == type)
            //    {
            //        this.AssociateElements.Remove(item);
            //        break;
            //    }
            //}
            //this.AssociateElements.Add(associateElement);
        }
        private List<LcPipe> GetCrossWalls(Line2d line2d)
        {
            var lcElements = Document.ModelSpace.Elements.Where((ele) => ele.Type == WaterElementType.Pipe).ToList();
            List<LcPipe> lcPipes = new List<LcPipe>();
            foreach (LcPipe wall in lcElements)
            {
                if (line2d != wall.BaseLine)
                {
                    if (wall.BaseLine.Start.Similarity(line2d.Start) || wall.BaseLine.Start.Similarity(line2d.End) || wall.BaseLine.End.Similarity(line2d.Start) || wall.BaseLine.End.Similarity(line2d.End))
                    {
                        lcPipes.Add(wall);
                    }
                    else
                        if (Line2d.CrossLine(line2d, wall.BaseLine))
                    {
                        lcPipes.Add(wall);
                    }
                }
            }
            return lcPipes;
        }

        public override LcElement ApplyMatrix(Matrix3 matrix)
        {
            BaseLine.Start = matrix.MultiplyPoint(BaseLine.Start);
            BaseLine.End = matrix.MultiplyPoint(BaseLine.End);
            ResetShapeCurves();
            return this;
        }

        public override LcElement Clone()
        {
            var clone = Document.CreateObject<LcPipe>();
            clone.Copy(this);
            clone.Initilize(this.Document);
            return clone;

        }

        public override void Copy(LcElement src)
        {
            base.Copy(src);
            var wall = ((LcPipe)src);
            this.Curves.Add(wall.BaseLine.Clone());
        }

        public void Set(Vector2 start = null, Vector2 end = null, bool fireChangedEvent = true)
        {
            //PropertySetter:Start,End
            if (!fireChangedEvent)
            {
                if (start != null) BaseLine.Start = start;
                if (end != null) BaseLine.End = end;
            }
            else
            {
                bool chg_start = (start != null && start != BaseLine.Start);
                if (chg_start)
                {
                    OnPropertyChangedBefore(nameof(BaseLine.Start), BaseLine.Start, start);
                    var oldValue = BaseLine.Start;
                    BaseLine.Start = start;
                    OnPropertyChangedAfter(nameof(BaseLine.Start), oldValue, BaseLine.Start);
                }
                bool chg_end = (end != null && end != BaseLine.End);
                if (chg_end)
                {
                    OnPropertyChangedBefore(nameof(BaseLine.End), BaseLine.End, end);
                    var oldValue = BaseLine.End;
                    BaseLine.End = end;
                    OnPropertyChangedAfter(nameof(BaseLine.End), oldValue, BaseLine.End);
                }
            }
        }

        protected override void OnPropertyChanged(string propertyName, LcPropertyGroup extPropGroup, LcProperty extProp)
        {
            base.OnPropertyChanged(propertyName, extPropGroup, extProp);
            ResetShapeCurves();
        }
        public override Box2 GetBoundingBox()
        {
            return new Box2().SetFromPoints(BaseLine.Start, BaseLine.End);
        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            return new Box2().SetFromPoints(matrix.MultiplyPoint(BaseLine.Start), matrix.MultiplyPoint(BaseLine.End));
        }
        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                //如果元素盒子，与多边形盒子不相交，那就可能不相交
                return false;
            }
            return GeoUtils.IsPolygonIntersectLine(testPoly.Points, BaseLine.Start, BaseLine.End)
                || GeoUtils.IsPolygonContainsLine(testPoly.Points, BaseLine.Start, BaseLine.End);
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            return GeoUtils.IsPolygonContainsLine(testPoly.Points, BaseLine.Start, BaseLine.End);
        }

        public override void Translate(double dx, double dy)
        {
            var ts = new Vector2(BaseLine.Start.X + dx, BaseLine.Start.Y + dy);
            var te = new Vector2(BaseLine.End.X + dx, BaseLine.End.Y + dy);
            Set(ts, te);
            ResetBoundingBox();
            ResetShapeCurves();

        }
        public override void Move(Vector2 startPoint, Vector2 endPoint)
        {
            Matrix3 matrix3 = Matrix3.GetMove(startPoint, endPoint);
            this.Set(matrix3.MultiplyPoint(BaseLine.Start), matrix3.MultiplyPoint(BaseLine.End));
            ResetBoundingBox();
            ResetShapeCurves();
        }
        public override void Scale(Vector2 basePoint, double scaleFactor)
        {
            Matrix3 matrix3 = Matrix3.GetScale(scaleFactor, basePoint);
            this.Set(matrix3.MultiplyPoint(BaseLine.Start), matrix3.MultiplyPoint(BaseLine.End));
            ResetBoundingBox();
            ResetShapeCurves();
        }
        public override void Scale(Vector2 basePoint, Vector2 scaleVector)
        {

            Matrix3 matrix3 = Matrix3.GetScale(scaleVector, basePoint);
            this.Set(matrix3.MultiplyPoint(BaseLine.Start), matrix3.MultiplyPoint(BaseLine.End));
            ResetBoundingBox();
            ResetShapeCurves();
        }
        public override void Rotate(Vector2 basePoint, double rotateAngle)
        {
            Matrix3 matrix3 = Matrix3.RotateInRadian(rotateAngle, basePoint);
            this.Set(matrix3.MultiplyPoint(BaseLine.Start), matrix3.MultiplyPoint(BaseLine.End));
            ResetBoundingBox();
            ResetShapeCurves();
        }
        public override void Mirror(Vector2 axisStart, Vector2 axisEnd)
        {
            Matrix3 matrix3 = Matrix3.GetMirror(axisEnd, axisStart);
            this.Set(matrix3.MultiplyPoint(BaseLine.Start), matrix3.MultiplyPoint(BaseLine.End));
            ResetBoundingBox();
            ResetShapeCurves();
        }
        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            base.WriteProperties(writer, soptions);

            writer.WriteNumberProperty(nameof(Width), this.Width);

            if (this.Height != 0)
            {
                writer.WriteNumberProperty(nameof(Height), this.Height);
            }
            if (this.Bottom != 0)
            {
                writer.WriteNumberProperty(nameof(Bottom), this.Bottom);
            }
            if (this.CenterOffset != 0)
            {
                writer.WriteNumberProperty(nameof(CenterOffset), this.CenterOffset);
            }
            if (this.IsReverse)
            {
                writer.WriteBoolProperty(nameof(IsReverse), this.IsReverse);
            }
        }
        public override void ReadProperties(ref JsonElement jele)
        {
            base.ReadProperties(ref jele);

            this.Width = jele.ReadDoubleProperty(nameof(Width));

            this.Height = jele.ReadDoubleProperty(nameof(Height));
            this.Bottom = jele.ReadDoubleProperty(nameof(Bottom));
            this.CenterOffset = jele.ReadDoubleProperty(nameof(CenterOffset));

            var isRev = jele.ReadBoolProperty(nameof(IsReverse));
            if (isRev != null) this.IsReverse = isRev.Value;
        }
    }
}
